{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# DPA介绍以及汉明设想\n",
    "\n",
    "支持的设备:\n",
    "\n",
    "SCOPES:\n",
    "\n",
    "* OPENADC\n",
    "\n",
    "PLATFORMS:\n",
    "\n",
    "* CWLITEARM\n",
    "* CWLITEXMEGA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "CRYPTO_TARGET = 'TINYAES128C'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../../hardware/victims/firmware/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DPA攻击理论\n",
    "\n",
    "首先建议你完成PA_Intro_1和PA_Intro_2，因为这将介绍Jupyter并展示如何使用Python与ChipWhisperer接口。假设你已经完成了这些，让我们来看看我们在这里要完成什么。回到理论上来，记得我们在数据线上的功率和测量的功耗之间有一个假设的关系。你可以在下面看到这一点：\n",
    "\n",
    "![](img/dpa_4bits_powerhw_scaled.png)\n",
    "\n",
    "我们如何证明这是真的呢？让我们绘制数据的汉明重量(HW)，与能量轨迹一起弄清楚这个问题! 我们将使用AES算法（使用什么并不重要），因为这是一个容易被用作我们攻击的一部分的固件。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 捕获能量轨迹"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "捕获功率跟踪将与以前的教程非常相似，只是这次我们将使用一个循环来捕获多个跟踪，以及使用numpy来存储它们。这不是必须的，但我们也会用`bokeh`绘制我们得到的轨迹。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 设置"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将使用一些辅助脚本来使设置和编程更容易。如果你使用的是XMEGA或STM（CWLITEARM）目标，带有正确的二进制文件应该为你设置："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = \"../../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex\".format(PLATFORM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 捕获能轨"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面你可以看到捕获循环。循环的主体加载一些新的明文，设置域端，发送密钥和明文，然后最后记录并将我们的新轨迹添加到`traces[]`列表中。在最后，我们将轨迹数据转换为numpy数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/plot.ipynb\"\n",
    "plot = real_time_plot(plot_len=3000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Capture Traces\n",
    "from tqdm import tnrange\n",
    "import numpy as np\n",
    "import time\n",
    "\n",
    "ktp = cw.ktp.Basic()\n",
    "\n",
    "traces = []\n",
    "N = 1000  # Number of traces\n",
    "\n",
    "for i in tnrange(N, desc='Capturing traces'):\n",
    "    key, text = ktp.next()  # manual creation of a key, text pair can be substituted here\n",
    "\n",
    "    trace = cw.capture_trace(scope, target, text, key)\n",
    "    \n",
    "    if trace is None:\n",
    "        continue\n",
    "    traces.append(trace)\n",
    "    plot.send(trace)\n",
    "\n",
    "#Convert traces to numpy arrays\n",
    "trace_array = np.asarray([trace.wave for trace in traces])  # if you prefer to work with numpy array for number crunching\n",
    "textin_array = np.asarray([trace.textin for trace in traces])\n",
    "known_keys = np.asarray([trace.key for trace in traces])  # for fixed key, these keys are all the same"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们有了我们的轨迹，我们也可以单独绘制它们："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "import matplotlib.pylab as plt\n",
    "plt.plot(trace_array[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup the connection to the target and scope\n",
    "scope.dis()\n",
    "target.dis()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 轨迹分析"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用轨迹数据"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在记录的能轨中，`trace_array`是轨迹数据，`textin_array`是加密的明文。现在我们来获取一些基本信息以便之后使用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numtraces = np.shape(trace_array)[0] #轨迹总数\n",
    "numpoints = np.shape(trace_array)[1] #每个轨迹的记录数"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了进行分析，我们需要循环分析我们要攻击的密钥中的每一个字节，以及每一个轨迹：\n",
    "```python\n",
    "for bnum in range(0, 16):\n",
    "    for tnum in range(0, numtraces):\n",
    "        pass\n",
    "```\n",
    "虽然我们没有对它们进行循环，但请注意，每条轨迹都是由一堆采样点组成的。让我们仔细看看AES，这样我们就可以用一些实际的代码来替换那个`pass'。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 计算汉明重量\n",
    "\n",
    "现在，我们已经有了一些可以使用的目标能量轨迹，我们可以继续进行下一步的攻击。回顾一下AES的工作原理，请记住，我们实际上是在试图瞄准这个图中底部的位置：\n",
    "\n",
    "    ![](img/Sbox_cpa_detail.png)\n",
    "\n",
    "因此，目标是确定S盒的输出，其中S盒的定义如下。我们的攻击点则在`SubBytes`也就是S盒置换之后，之前在测量信噪比一章中已经提到了这点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbox = (\n",
    "    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\n",
    "    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\n",
    "    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\n",
    "    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\n",
    "    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\n",
    "    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\n",
    "    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\n",
    "    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\n",
    "    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\n",
    "    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\n",
    "    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\n",
    "    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\n",
    "    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\n",
    "    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\n",
    "    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\n",
    "    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以我们可以编写一个函数计算一个输入字节，它的输出便是S盒的输出："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def intermediate(pt, keyguess):\n",
    "    return sbox[pt ^ keyguess]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，记住我们要的是S盒输出的汉明重量。我们的假设是，系统正在泄露该S-Box输出的汉明重量。作为一个愚蠢的解决方案，我们可以首先将每个数字转换为二进制，然后计算1的数量。\n",
    "\n",
    "```python\n",
    ">>> bin(0x1F)\n",
    "'0b11111'\n",
    ">>> bin(0x1F).count('1')\n",
    "5\n",
    "```\n",
    "这最终将是相当缓慢的。我们可以做一个查找表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "HW = [bin(n).count(\"1\") for n in range(0, 256)]\n",
    "\n",
    "def intermediate(pt, key):\n",
    "    return sbox[pt ^ key]\n",
    "\n",
    "#Example - PlainText is 0x12, key is 0xAB\n",
    "HW[intermediate(0x12, 0xAB)]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 绘制HW图像"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，我们要做的是把每个不同的\"类别\"用不同的颜色绘制出来。通过这个，我们应该看到是否有一些位置在汉明重量上有相对明显的差异。我们使用前面定义的\"HW\"数组和\"intermediate()\"函数，以及一个绘制所有轨迹的循环，可以很容易地得到这个结果。\n",
    "\n",
    "为了使之更容易，我们可以放大一些特定的区域。在下面的例子中，只绘制了全部捕获的一个小子集。你可以通过使用CPA攻击（我们后面会讲到）更容易地找出这个点，它提供了更多关于泄漏发生的地方的信息。现在让我们假设我们已经知道什么是\"好\"点了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.palettes import brewer\n",
    "\n",
    "output_notebook()\n",
    "p = figure()\n",
    "\n",
    "#Must run S-Box() script first to define the HW[] array and intermediate() function\n",
    "\n",
    "#Must adjust these points for different compilers/targets\n",
    "if PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    plot_start = 1300\n",
    "    plot_end = 1400\n",
    "elif PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    plot_start = 1370\n",
    "    plot_end = 1410\n",
    "elif PLATFORM == \"CWNANO\":\n",
    "    plot_start = 590\n",
    "    plot_end = 620\n",
    "\n",
    "\n",
    "xrange = range(len(traces[0].wave))[plot_start:plot_end]\n",
    "bnum = 0\n",
    "\n",
    "color_mapper = brewer['Reds'][9]\n",
    "\n",
    "for trace in traces:\n",
    "    hw_of_byte = HW[intermediate(trace.textin[bnum], trace.key[bnum])]\n",
    "    p.line(xrange, trace.wave[plot_start:plot_end], line_color=color_mapper[hw_of_byte])\n",
    "\n",
    "show(p)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 找到平均值\n",
    "\n",
    "要在这些汉明重量值和电压中找到“最好的地方”我们可以编写一个循环去检查每一个点的相关性并且找到最大值。在这么多点中我们只需要选取一个[500,1000]左右区间即可。下面展示了如何从足够的点中找到想要的相关值，通常我们不关心相关值的正负性而是直接使用绝对值去寻找最大值。\n",
    "这里的“最好的地方”指的是上一步绘制HW中选取的特殊区域（some specific area），以便在后面更好的进行攻击。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "max_corr = 0\n",
    "point = 0\n",
    "for avg_point in range(500, 1400):\n",
    "    hw_list = [[], [], [], [], [], [], [], [], []]\n",
    "    for trace in traces:\n",
    "        hw_of_byte = HW[intermediate(trace.textin[bnum], trace.key[bnum])]\n",
    "        hw_list[hw_of_byte].append(trace.wave[avg_point])\n",
    "    \n",
    "    hw_mean_list = [np.mean(hw_list[i]) for i in range(0, 9)]\n",
    "    \n",
    "    corr = abs(np.corrcoef(range(1,9), hw_mean_list[1:9])[0,1])\n",
    "    if corr > max_corr:\n",
    "        max_corr = corr\n",
    "        point = avg_point\n",
    "        \n",
    "print(max_corr, point)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果你回到之前的图，看看相关性最高的那一点，你会发现你可以通过视觉上寻找图上明显的颜色梯度来确定好的点来尝试。尝试一些没有明显梯度的不同的点，看看在后面的步骤中，汉明重量与电压的关系图是怎样的。\n",
    "\n",
    "因此，有了存在差异的想法，让我们实际绘制它们，看看它们在现实生活中是多么\"线性\"。我们将再选择一个点，用它来获得平均数。下面将找到并打印平均数，这段代码之前也是用来确定相关性最高的那一点的。我们现在用它来评估具有最高相关度的点的电压与汉明重量值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# from point of max correlation\n",
    "avg_point = point\n",
    "\n",
    "hw_list = [[], [], [], [], [], [], [], [], []]\n",
    "for trace in traces:\n",
    "    hw_of_byte = HW[intermediate(trace.textin[bnum], trace.key[bnum])]\n",
    "    hw_list[hw_of_byte].append(trace.wave[avg_point])\n",
    "    \n",
    "hw_mean_list = [np.mean(hw_list[i]) for i in range(0, 9)]\n",
    "    \n",
    "for hw in range(1, 9):\n",
    "    print(\"HW \" + str(hw) + \": \" + str(hw_mean_list[hw]))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述情况看起来应该有些线性。让我们绘制一个漂亮的图来看看它的视觉效果。如果它是线性的，请尝试一些其他的点，看看它们如何比较："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.io import output_notebook\n",
    "\n",
    "output_notebook()\n",
    "p = figure(title=\"HW vs 电压\")\n",
    "p.line(range(1, 9), hw_mean_list[1:9], line_color=\"red\")\n",
    "p.xaxis.axis_label = \"汉明重量\"\n",
    "p.yaxis.axis_label = \"平均电压\"\n",
    "show(p)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么到这里其实我们已经看到了HW和电压的相关性了，当然我们发现斜率不符合预期，我们最开始的预期是HW值越大，电压越大，而这里则是相反，那是因为和我们的测量方式有关\n",
    "\n",
    "![](img/vmeasure.png)\n",
    "\n",
    "我们正在测量分流电阻上的压降。电流的增加导致电阻上的电压升高。当没有电流流动时，电阻上没有压降。但由于我们只测量电阻的一端，所以当没有电流时，我们看到的电压更高。\n",
    "\n",
    "我们可以通过简单地颠倒测量方向（在测量值前面加一个-）来解决这个斜率。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.12 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.9.12 (main, Mar 26 2022, 15:51:13) \n[Clang 12.0.0 (clang-1200.0.32.29)]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
